package com.xuyu.ddd.aggregate.core.persistence;

import com.baomidou.mybatisplus.extension.service.IService;
import com.xuyu.ddd.aggregate.core.aggregate.Aggregate;
import com.xuyu.ddd.aggregate.core.annotation.Persistence;
import com.xuyu.ddd.aggregate.core.annotation.PersistenceIgnore;
import com.xuyu.ddd.aggregate.core.field.Lazy;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 封装一些简化的工具类
 */
public class RepoUtil {

    public static <T> void updateOrInsert(List<T> update, List<T> insert, IService<T> iService) {
        if (!CollectionUtils.isEmpty(update)) {
            iService.updateBatchById(update);
        }

        if (!CollectionUtils.isEmpty(insert)) {
            iService.saveBatch(insert);
        }
    }

    public static <T> void doWhenNotNull(T param, Consumer<T> consumer) {
        if (param == null || consumer == null) {
            return;
        }

        consumer.accept(param);
    }

    /**
     * 获得变更的对象
     * @param old 老的值
     * @param current 当前的值
     * @param ignoredFields 忽略字段
     * @return 返回变更的字段名
     * @param <T> 领域对象
     */
    public static <T> Set<String> getChangedFields(T old, T current, String... ignoredFields) {
        Set<String> results = new HashSet<>();
        Set<String> ignoreFieldSet = new HashSet<>(Arrays.asList(ignoredFields));
        List<Field> fields = getAllField(current.getClass());
        for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(PersistenceIgnore.class)) {
                continue;
            }
            if (ignoreFieldSet.contains(field.getName())) {
                continue;
            }
            if (old == null) {
                results.add(field.getName());
                continue;
            }
            try {
                //最基本也得加@Data，这样才会重写equals方法！！！！！！！
                if (!Objects.equals(field.get(old), field.get(current))) {
                    results.add(field.getName());
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        return results;
    }


    public static List<Field> getAllField(Class<?> clazz) {
        // 获取本类中声明的所有字段
        Field[] fields = clazz.getDeclaredFields();
        List<Field> allField = new ArrayList<>(Arrays.asList(fields));
        // 获取父类中的所有字段
        Class<?> superclass = clazz.getSuperclass();
        if (superclass != null) {
            List<Field> superField = getAllField(superclass);
            allField.addAll(superField);
        }
        return allField;
    }


    /**
     * 自动持久化
     *
     * @param aggregate          聚合
     * @param applicationContext spring的上下文
     * @param ignore             有的时候，加了@Persistence的字段可能也无需持久化，所以传入ignore为字段名，这个优先级更高，直接忽略
     * @param <T>                聚合根类型
     */
    @SuppressWarnings("unchecked")
    public static <T extends Serializable, R extends Serializable> void autoPersistence(Aggregate<T> aggregate, ApplicationContext applicationContext, String... ignore) throws IllegalAccessException {
        Assert.notNull(aggregate, "聚合根不能为空");
        Assert.notNull(applicationContext, "spring上下文不能为空");
        Set<String> rootChangedField = aggregate.getRootChangedField();
        if (CollectionUtils.isEmpty(rootChangedField)) {
            return;
        }
        Class<?> aClass = aggregate.getRoot().getClass();
        Map<String, List<Field>> collect = getAllField(aClass).stream().collect(Collectors.groupingBy(Field::getName));
        List<String> ignoreList = Arrays.asList(ignore);
        for (String changedField : rootChangedField) {
            //双层过滤之一： 直接传入忽略字段
            if (ignoreList.contains(changedField)) {
                continue;
            }
            if (collect.containsKey(changedField)) {
                Field field = collect.get(changedField).get(0);
                //双层过滤之二：加了PersistenceIgnore注解的
                if (field.isAnnotationPresent(PersistenceIgnore.class)) {
                    continue;
                }
                field.setAccessible(true);
                Persistence[] annotationsByType = field.getAnnotationsByType(Persistence.class);
                if (annotationsByType.length == 0) {
                    continue;
                }
                Persistence persistence = annotationsByType[0];
                Class<?> repo = persistence.repo();
                IService bean = (IService) applicationContext.getBean(repo);
                Assert.notNull(bean, "持久化repo不能为null");
                Object value = field.get(aggregate.getRoot());

                //判断类型,如果是lazy
                if (value instanceof Lazy) {
                    //获取实际要持久化的对象数据
                    Lazy<R> lazy = (Lazy<R>) value;
                    R r = lazy.get();
                    if (r == null) {
                        //不需要操作
                        continue;
                    }
                    //继续判断实际类型
                    if (r instanceof Collection) {
                        //如果是数组类型
                        bean.saveOrUpdateBatch((List) r);
                    } else {
                        //通通认为是单个的对象
                        bean.saveOrUpdate(r);
                    }
                }
            }
        }
    }
}
